﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Test
{
	/// <summary>
	/// Optional interface for IQueryProvider to implement Query<T>'s QueryText property.
	/// </summary>
	public interface IQueryText
	{
		string GetQueryText(Expression expression);
	}

    /// <summary>
    /// Represents a query, where its Expression property is like an execution plan, and execution is deferred until
	/// the call to IQueryProvider.Execute.	
    /// </summary>
    public class Query<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable
    {
        IQueryProvider provider;
        Expression expression;
		long id;


		public Query(IQueryProvider provider)
			: this(provider: provider, id: 0) {
		}		
		public Query(IQueryProvider provider, long id) {
			this.expression = Expression.Constant(this);
			this.provider = provider;
			this.id = id;
		}
		public Query(IQueryProvider provider, 
			long id, Expression expression)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }            
			if (!(typeof(IQueryable<T>).IsAssignableFrom(expression.Type) || typeof(IEnumerable<T>).IsAssignableFrom(expression.Type)))
            {
                throw new ArgumentOutOfRangeException("expression");
            }
            this.provider = provider;
            this.expression = expression ?? Expression.Constant(this);
			this.id = id;
        }
		public long ID {
			get { return id; } 
		}
		
        public Expression Expression
        {
            get { return this.expression; }
        }

        public Type ElementType
        {
            get { return typeof(T); }
        }

        public IQueryProvider Provider
        {
            get { return this.provider; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator();		
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator();
        }
		
        public override string ToString()
        {
			return this.TSQL;
        }
		
		public string TSQL {
			get { return ((IQueryText)provider).GetQueryText(this.expression); }
		}

	}
}
